﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace SpiritEngine
{
    public class Engine
    {
        // MEMBER VARIABLES
        // ------------------------------------------------------------------------------------------------------------------------

        List<GameScreen> m_listGameScreens = new List<GameScreen>();

        Game m_GameObject;
        GameTime m_gtGameTime = null;

        SE_ContentManager m_cmContentManager = null;
        GraphicsDevice m_gdGraphicsDevice = null;
        SpriteBatch m_sbSpriteBatch = null;
        IServiceContainer m_IscServiceContainer = null;

        CID_Keyboard m_Keyboard = null;
        CID_Mouse m_Mouse = null;
        CID_XBox360Gamepad m_XboxGamepad1 = null;

        SpriteFont m_SpriteFont;

        Texture2D m_Blank;  // This is used for drawing 2D lines.


        bool m_bFullScreen = false;


        // CONSTRUCTORS
        // ------------------------------------------------------------------------------------------------------------------------

        public Engine(GraphicsDeviceManager Graphics)
        {
            m_IscServiceContainer = new ServiceContainer();
            Services.AddService(typeof(IGraphicsDeviceService), Graphics);
            Services.AddService(typeof(GraphicsDeviceManager), Graphics);

            this.m_gdGraphicsDevice = Graphics.GraphicsDevice;

            m_cmContentManager = new SE_ContentManager(m_IscServiceContainer);
            m_sbSpriteBatch = new SpriteBatch(m_gdGraphicsDevice);


            // Create our input objects.
            m_Keyboard = new CID_Keyboard();
            m_Mouse = new CID_Mouse(this);
            m_XboxGamepad1 = new CID_XBox360Gamepad(PlayerIndex.One);

            // Add them into the engine's services container
            Services.AddService(typeof(CID_Keyboard), m_Keyboard);
            Services.AddService(typeof(CID_Mouse), m_Mouse);


            // Setup a small texture to use for line drawing.
            m_Blank = new Texture2D(m_gdGraphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
            m_Blank.SetData(new[] { Color.White });


            // Load the Engine's default SpriteFont
            m_SpriteFont = Content.Load<SpriteFont>("Content\\Engine01");


        } // end Engine::Engine(GraphicsDeviceManager)


        // PRIVATE/PROTECTED METHODS
        // ------------------------------------------------------------------------------------------------------------------------



        // METHODS
        // ------------------------------------------------------------------------------------------------------------------------

        public void PushGameScreen(GameScreen GameScreen)
        {
            // Only allow GameScreens to exist in one instance of the Engine at a time.
            if (GameScreen.Engine != null)
            {
                throw new Exception("This GameScreen already exists on the stack of another instance of the Engine!");
            }


            if (!m_listGameScreens.Contains(GameScreen))
            {
                m_listGameScreens.Add(GameScreen);
                GameScreen.m_Engine = this;
                GameScreen.LoadGameScreen();
            }

        } // end void Engine::PushGameScreen(GameScreen)

        public GameScreen PopGameScreen()
        {
            if (m_listGameScreens.Count == 0)
                return null;

            GameScreen screen = m_listGameScreens[m_listGameScreens.Count - 1];

            // Unlink this GameScreen object from this instance of the Engine before removing
            // it from the Engine's GameScreen stack.
            screen.m_Engine = null;

            // Return the top Gamescreen from the stack
            return screen;

        } // end GameScreen Engine::PopGameScreen()

        public void Update(GameTime GameTime)
        {
            this.m_gtGameTime = GameTime;

            m_Keyboard.Update();
            m_Mouse.Update();
            m_XboxGamepad1.Update();


            // Copy the list of GameScreens so the game won't crash if the original
            // list is modified while we're updating.
            List<GameScreen> copy = new List<GameScreen>();
            
            foreach (GameScreen screen in m_listGameScreens)
                copy.Add(screen);

            foreach (GameScreen screen in copy)
                screen.Update();

        } // end void Engine::Update(GameTime)

        public void Draw(GameTime GameTime)
        {
            this.m_gtGameTime = GameTime;

            foreach (GameScreen screen in m_listGameScreens)
                screen.Draw();

        } // end void Engine::Draw(GameTime)

        public void DrawLine(float width, Color color, Vector2 point1, Vector2 point2)
        {
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
            float length = Vector2.Distance(point1, point2);

            SpriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                                            SpriteSortMode.Deferred,
                                            SaveStateMode.None);

            m_sbSpriteBatch.Draw(m_Blank, point1, null, color, angle,
                                 new Vector2(0.5f, 0.5f), new Vector2(length, width),
                                 SpriteEffects.None, 0);

            SpriteBatch.End();
        }

        public void DrawString(string strText, Vector2 vPosition, Color color)
        {
            SpriteBatch.Begin();
            SpriteBatch.DrawString(m_SpriteFont, strText, vPosition, color);
            SpriteBatch.End();
        }



        // DEBUG METHODS
        // ------------------------------------------------------------------------------------------------------------------------

#if (DEBUG)
        // put all debug methods in here...
#endif



        // PROPERTIES
        // ------------------------------------------------------------------------------------------------------------------------

        public SE_ContentManager Content
        {
            get
            {
                return m_cmContentManager;
            }
        } // end property Content

        public bool Fullscreen
        {
            get
            {
                return m_bFullScreen;
            }
            set
            {
                GraphicsDeviceManager service = (GraphicsDeviceManager)Services.GetService(typeof(GraphicsDeviceManager));

                if (service == null)
                {
                    return;
                }


                if (value == (!m_bFullScreen))
                {
                    m_bFullScreen = value;
                    service.ToggleFullScreen();
                }
            }
        }

        public Game Game
        {
            get
            {
                return m_GameObject;
            }
            set
            {
                if (value != null)
                    m_GameObject = value;
            }
        }

        public GameTime GameTime
        {
            get
            {
                return m_gtGameTime;
            }
        } // end property GameTime

        public GraphicsDevice GraphicsDevice
        {
            get
            {
                return m_gdGraphicsDevice;
            }
        } // end property GraphicsDevice

        public IServiceContainer Services
        {
            get
            {
                return m_IscServiceContainer;
            }
        } // end property Services

        public int ScreenHeight
        {
            get
            {
                return GraphicsDevice.Viewport.Height;
            }
        } // end property ScreenHeight

        public int ScreenWidth
        {
            get
            {
                return GraphicsDevice.Viewport.Width;
            }
        } // end property ScreenWidth

        public SpriteBatch SpriteBatch
        {
            get
            {
                return m_sbSpriteBatch;
            }
        } // end property SpriteBatch

        public SpriteFont SpriteFont
        {
            get
            {
                return m_SpriteFont;
            }
            set
            {
                m_SpriteFont = value;
            }
        } // end property SpriteFont


        // EVENTS
        // ------------------------------------------------------------------------------------------------------------------------



    } // end class Engine
}
